<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>打不死的回调地狱？ | 网站技术分享</title>
    <meta name="generator" content="VuePress 1.7.1">
    <link rel="icon" href="/ztjy-technology/favicon.ico">
    <meta name="description" content="">
    
    <link rel="preload" href="/ztjy-technology/assets/css/0.styles.342aeebf.css" as="style"><link rel="preload" href="/ztjy-technology/assets/js/app.ec27be4d.js" as="script"><link rel="preload" href="/ztjy-technology/assets/js/2.98a51c7c.js" as="script"><link rel="preload" href="/ztjy-technology/assets/js/9.5bd259cf.js" as="script"><link rel="prefetch" href="/ztjy-technology/assets/js/3.f94f03dc.js"><link rel="prefetch" href="/ztjy-technology/assets/js/4.e44f57a3.js"><link rel="prefetch" href="/ztjy-technology/assets/js/5.65aa5b3a.js"><link rel="prefetch" href="/ztjy-technology/assets/js/6.c9f44ce5.js"><link rel="prefetch" href="/ztjy-technology/assets/js/7.5886c06f.js"><link rel="prefetch" href="/ztjy-technology/assets/js/8.6a311f92.js">
    <link rel="stylesheet" href="/ztjy-technology/assets/css/0.styles.342aeebf.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/ztjy-technology/" class="home-link router-link-active"><!----> <span class="site-name">网站技术分享</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="html&amp;css" class="dropdown-title"><span class="title">html&amp;css</span> <span class="arrow down"></span></button> <button type="button" aria-label="html&amp;css" class="mobile-dropdown-title"><span class="title">html&amp;css</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/Carousel/" class="nav-link">
  如何设计实现无缝轮播
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/dialog/" class="nav-link">
  实现一个 Dialog 类）
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/BFC/" class="nav-link">
  BFC、IFC、GFC 和 FFC
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/overflow/" class="nav-link">
  文本溢出
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="javascript" class="dropdown-title"><span class="title">javascript</span> <span class="arrow down"></span></button> <button type="button" aria-label="javascript" class="mobile-dropdown-title"><span class="title">javascript</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js的面向对象编程/" class="nav-link">
  js的面向对象编程
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/ES5/ES6 的继承除了写法以外还有什么区别？/" class="nav-link">
  ES5/ES6 的继承除了写法以外还有什么区别？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/用深度优先思想和广度优先思想实现一个拷贝函数？/" class="nav-link">
  用深度优先思想和广度优先思想实现一个拷贝函数？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/如何实现一个 new/" class="nav-link">
  如何实现一个 new
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js原型和原型链以及应用/" class="nav-link">
  js原型和原型链以及应用
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js作用域提升和闭包/" class="nav-link">
  js作用域提升和闭包
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es6中模拟map源码/" class="nav-link">
  es6中模拟map源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟call源码/" class="nav-link">
  模拟call源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟bind源码/" class="nav-link">
  模拟bind源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟apply源码/" class="nav-link">
  模拟apply源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/generator/" class="nav-link">
  generator
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/promise/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  promise 以及promise的应用 异步加载文件
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/设计并实现 Promise.race()/" class="nav-link">
  设计并实现 Promise.race()
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟实现一个 Promise.finally/" class="nav-link">
  模拟实现一个 Promise.finally
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/async await 以及优雅的错误处理/" class="nav-link">
  async await 以及优雅的错误处理
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/Async/Await 如何通过同步的方式实现异步/" class="nav-link">
  Async/Await 如何通过同步的方式实现异步
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/co模块的封装/" class="nav-link">
  co模块的封装
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/柯理化函数/" class="nav-link">
  柯理化函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/Object.defineProperty缺陷以及proxy/" class="nav-link">
  Object.defineProperty缺陷以及proxy
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es7中装饰器/" class="nav-link">
  es7中装饰器
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/数组去重的八种姿势/" class="nav-link">
  数组去重的八种姿势
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/set、map、weakSet、weakMap区别/" class="nav-link">
  set、map、weakSet、weakMap区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es6转换es5的实现思路/" class="nav-link">
  es6转换es5的实现思路
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/for循环和forEach性能研究/" class="nav-link">
  for循环和forEach性能研究
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/浏览器和 Node 事件循环的区别/" class="nav-link">
  浏览器和 Node 事件循环的区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/内存泄漏 OOM 垃圾回收机制/" class="nav-link">
  内存泄漏 OOM 垃圾回收机制
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="vue" class="dropdown-title"><span class="title">vue</span> <span class="arrow down"></span></button> <button type="button" aria-label="vue" class="mobile-dropdown-title"><span class="title">vue</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/vue混入/" class="nav-link">
  vue混入
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/封装一个全局模态框/" class="nav-link">
  封装一个全局模态框
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的插件机制/" class="nav-link">
  Vue 的插件机制
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的双向数据绑定，Model 如何改变 View，View 又是如何改变 Model 的/" class="nav-link">
  Vue 的双向数据绑定，Model 如何改变 View，View 又是如何改变 Model 的
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的父组件和子组件生命周期钩子执行顺序是什么/" class="nav-link">
  Vue 的父组件和子组件生命周期钩子执行顺序是什么
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Virtual DOM 真的比操作原生 DOM 快吗？谈谈你的想法。/" class="nav-link">
  Virtual DOM 真的比操作原生 DOM 快吗？谈谈你的想法。
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/虚拟dom/" class="nav-link">
  虚拟dom
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/写 React / Vue 项目时为什么要在列表组件中写 key，其作用是什么？/" class="nav-link">
  写 React / Vue 项目时为什么要在列表组件中写 key，其作用是什么？
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="react" class="dropdown-title"><span class="title">react</span> <span class="arrow down"></span></button> <button type="button" aria-label="react" class="mobile-dropdown-title"><span class="title">react</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/redux 为什么要把 reducer 设计成纯函数/" class="nav-link">
  redux 为什么要把 reducer 设计成纯函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/react-router和 a 标签有什么区别/" class="nav-link">
  react-router和 a 标签有什么区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作？/" class="nav-link">
  为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/hooks/" class="nav-link">
  hooks
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="node" class="dropdown-title"><span class="title">node</span> <span class="arrow down"></span></button> <button type="button" aria-label="node" class="mobile-dropdown-title"><span class="title">node</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/npm 模块安装机制，为什么输入 npm install 就可以自动安装对应的模块？/" class="nav-link">
  npm 模块安装机制，为什么输入 npm install 就可以自动安装对应的模块？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/node中并发和多线程的理解/" class="nav-link">
  node中并发和多线程的理解
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/浏览器和 Node 事件循环的区别/" class="nav-link">
  浏览器和 Node 事件循环的区别
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="网络编程" class="dropdown-title"><span class="title">网络编程</span> <span class="arrow down"></span></button> <button type="button" aria-label="网络编程" class="mobile-dropdown-title"><span class="title">网络编程</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http协议 http1.0、1.1、2.0 协议的区别？/" class="nav-link">
  http协议 http1.0、1.1、2.0 协议的区别？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/Http 状态码 301 和 302 的应用场景分别是什么/" class="nav-link">
  Http 状态码 301 和 302 的应用场景分别是什么
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/永久性重定向（301）和临时性重定向（302）对 SEO 有什么影响/" class="nav-link">
  永久性重定向（301）和临时性重定向（302）对 SEO 有什么影响
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http缓存/" class="nav-link">
  http缓存
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/介绍下前端加密的常见场景和方法/" class="nav-link">
  介绍下前端加密的常见场景和方法
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/HTTPS 中间人攻击/" class="nav-link">
  HTTPS 中间人攻击
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/接口如何防刷/" class="nav-link">
  接口如何防刷
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/浏览器缓存读取规则/" class="nav-link">
  浏览器缓存读取规则
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/websocket/" class="nav-link">
  websocket
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/TCP三次握手和四次挥手的理解/" class="nav-link">
  TCP三次握手和四次挥手的理解
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/cookie 和 token 都存放在 header 中，为什么不会劫持 token？/" class="nav-link">
  cookie 和 token 都存放在 header 中，为什么不会劫持 token？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/如何实现 token 加密/" class="nav-link">
  如何实现 token 加密
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http2的多路复用/" class="nav-link">
  http2的多路复用
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="优化" class="dropdown-title"><span class="title">优化</span> <span class="arrow down"></span></button> <button type="button" aria-label="优化" class="mobile-dropdown-title"><span class="title">优化</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/重绘和回流/" class="nav-link">
  重绘和回流
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/防抖和节流  input 搜索如何防抖，如何处理中文输入/" class="nav-link">
  防抖和节流  input 搜索如何防抖，如何处理中文输入
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/webpack打包vue速度太慢怎么解决/" class="nav-link">
  webpack打包vue速度太慢怎么解决
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/如何优化首页的加载速度？首页白屏是什么问题引起的？如何解决呢/" class="nav-link">
  如何优化首页的加载速度？首页白屏是什么问题引起的？如何解决呢
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/vue 渲染大量数据时应该怎么优化？/" class="nav-link">
  vue 渲染大量数据时应该怎么优化？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/webpack 热更新原理，是如何做到在不刷新浏览器的前提下更新页面的/" class="nav-link">
  webpack 热更新原理，是如何做到在不刷新浏览器的前提下更新页面的
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="工程化" class="dropdown-title"><span class="title">工程化</span> <span class="arrow down"></span></button> <button type="button" aria-label="工程化" class="mobile-dropdown-title"><span class="title">工程化</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/文档工具/" class="nav-link">
  文档工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/git的workflow/" class="nav-link">
  git的workflow
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/接口测试工具/" class="nav-link">
  接口测试工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/敏捷开发工具/" class="nav-link">
  敏捷开发工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/线上bug的解决姿势/" class="nav-link">
  线上bug的解决姿势
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/ts的环境搭建/" class="nav-link">
  ts的环境搭建
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/Nginx/" class="nav-link">
  Nginx
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="多端开发" class="dropdown-title"><span class="title">多端开发</span> <span class="arrow down"></span></button> <button type="button" aria-label="多端开发" class="mobile-dropdown-title"><span class="title">多端开发</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/andorid和js的交互/" class="nav-link">
  andorid和js的交互
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/flutter/" class="nav-link">
  flutter
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/react-native/" class="nav-link">
  react-native
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="面试题" class="dropdown-title"><span class="title">面试题</span> <span class="arrow down"></span></button> <button type="button" aria-label="面试题" class="mobile-dropdown-title"><span class="title">面试题</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现模糊搜索结果的关键词高亮显示/" class="nav-link">
  实现模糊搜索结果的关键词高亮显示
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现 convert 方法，把原始 list 转换成树形结构，要求尽可能降低时间复杂度/" class="nav-link">
  实现 convert 方法，把原始 list 转换成树形结构，要求尽可能降低时间复杂度
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/已知数据格式，实现一个函数 fn 找出链条中所有的父级 id/" class="nav-link">
  已知数据格式，实现一个函数 fn 找出链条中所有的父级 id
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/移动端 Retina 屏 1px 像素问题/" class="nav-link">
  移动端 Retina 屏 1px 像素问题
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/算法题之移动零/" class="nav-link">
  算法题之移动零
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/模拟实现一个深拷贝，并考虑对象相互引用以及 Symbol 拷贝的情况/" class="nav-link">
  模拟实现一个深拷贝，并考虑对象相互引用以及 Symbol 拷贝的情况
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片？/" class="nav-link">
  为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/算法题「旋转数组」/" class="nav-link">
  算法题「旋转数组」
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/两个数组合并成一个数组/" class="nav-link">
  两个数组合并成一个数组
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/a.b.c.d` 和 a[&quot;b&quot;][&quot;c&quot;][&quot;d&quot;]，哪个性能更高？/" class="nav-link">
  a.b.c.d 和 a[&quot;b&quot;][&quot;c&quot;][&quot;d&quot;]，哪个性能更高？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现一个 sleep 函数/" class="nav-link">
  实现一个 sleep 函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/如何把一个字符串的大小写取反（大写变小写小写变大写），例如 &quot;AbC&quot; 变成 &quot;aBc&quot; 。/" class="nav-link">
  如何把一个字符串的大小写取反（大写变小写小写变大写），例如 &quot;AbC&quot; 变成 &quot;aBc&quot; 。
</a></li></ul></div></div> <!----></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="html&amp;css" class="dropdown-title"><span class="title">html&amp;css</span> <span class="arrow down"></span></button> <button type="button" aria-label="html&amp;css" class="mobile-dropdown-title"><span class="title">html&amp;css</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/Carousel/" class="nav-link">
  如何设计实现无缝轮播
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/dialog/" class="nav-link">
  实现一个 Dialog 类）
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/BFC/" class="nav-link">
  BFC、IFC、GFC 和 FFC
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/html/overflow/" class="nav-link">
  文本溢出
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="javascript" class="dropdown-title"><span class="title">javascript</span> <span class="arrow down"></span></button> <button type="button" aria-label="javascript" class="mobile-dropdown-title"><span class="title">javascript</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js的面向对象编程/" class="nav-link">
  js的面向对象编程
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/ES5/ES6 的继承除了写法以外还有什么区别？/" class="nav-link">
  ES5/ES6 的继承除了写法以外还有什么区别？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/用深度优先思想和广度优先思想实现一个拷贝函数？/" class="nav-link">
  用深度优先思想和广度优先思想实现一个拷贝函数？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/如何实现一个 new/" class="nav-link">
  如何实现一个 new
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js原型和原型链以及应用/" class="nav-link">
  js原型和原型链以及应用
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/js作用域提升和闭包/" class="nav-link">
  js作用域提升和闭包
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es6中模拟map源码/" class="nav-link">
  es6中模拟map源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟call源码/" class="nav-link">
  模拟call源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟bind源码/" class="nav-link">
  模拟bind源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟apply源码/" class="nav-link">
  模拟apply源码
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/generator/" class="nav-link">
  generator
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/promise/" aria-current="page" class="nav-link router-link-exact-active router-link-active">
  promise 以及promise的应用 异步加载文件
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/设计并实现 Promise.race()/" class="nav-link">
  设计并实现 Promise.race()
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/模拟实现一个 Promise.finally/" class="nav-link">
  模拟实现一个 Promise.finally
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/async await 以及优雅的错误处理/" class="nav-link">
  async await 以及优雅的错误处理
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/Async/Await 如何通过同步的方式实现异步/" class="nav-link">
  Async/Await 如何通过同步的方式实现异步
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/co模块的封装/" class="nav-link">
  co模块的封装
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/柯理化函数/" class="nav-link">
  柯理化函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/Object.defineProperty缺陷以及proxy/" class="nav-link">
  Object.defineProperty缺陷以及proxy
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es7中装饰器/" class="nav-link">
  es7中装饰器
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/数组去重的八种姿势/" class="nav-link">
  数组去重的八种姿势
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/set、map、weakSet、weakMap区别/" class="nav-link">
  set、map、weakSet、weakMap区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/es6转换es5的实现思路/" class="nav-link">
  es6转换es5的实现思路
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/for循环和forEach性能研究/" class="nav-link">
  for循环和forEach性能研究
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/浏览器和 Node 事件循环的区别/" class="nav-link">
  浏览器和 Node 事件循环的区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/js/内存泄漏 OOM 垃圾回收机制/" class="nav-link">
  内存泄漏 OOM 垃圾回收机制
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="vue" class="dropdown-title"><span class="title">vue</span> <span class="arrow down"></span></button> <button type="button" aria-label="vue" class="mobile-dropdown-title"><span class="title">vue</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/vue混入/" class="nav-link">
  vue混入
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/封装一个全局模态框/" class="nav-link">
  封装一个全局模态框
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的插件机制/" class="nav-link">
  Vue 的插件机制
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的双向数据绑定，Model 如何改变 View，View 又是如何改变 Model 的/" class="nav-link">
  Vue 的双向数据绑定，Model 如何改变 View，View 又是如何改变 Model 的
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Vue 的父组件和子组件生命周期钩子执行顺序是什么/" class="nav-link">
  Vue 的父组件和子组件生命周期钩子执行顺序是什么
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/Virtual DOM 真的比操作原生 DOM 快吗？谈谈你的想法。/" class="nav-link">
  Virtual DOM 真的比操作原生 DOM 快吗？谈谈你的想法。
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/虚拟dom/" class="nav-link">
  虚拟dom
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/vue/写 React / Vue 项目时为什么要在列表组件中写 key，其作用是什么？/" class="nav-link">
  写 React / Vue 项目时为什么要在列表组件中写 key，其作用是什么？
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="react" class="dropdown-title"><span class="title">react</span> <span class="arrow down"></span></button> <button type="button" aria-label="react" class="mobile-dropdown-title"><span class="title">react</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/redux 为什么要把 reducer 设计成纯函数/" class="nav-link">
  redux 为什么要把 reducer 设计成纯函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/react-router和 a 标签有什么区别/" class="nav-link">
  react-router和 a 标签有什么区别
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作？/" class="nav-link">
  为什么 Vuex 的 mutation 和 Redux 的 reducer 中不能做异步操作？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/react/hooks/" class="nav-link">
  hooks
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="node" class="dropdown-title"><span class="title">node</span> <span class="arrow down"></span></button> <button type="button" aria-label="node" class="mobile-dropdown-title"><span class="title">node</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/npm 模块安装机制，为什么输入 npm install 就可以自动安装对应的模块？/" class="nav-link">
  npm 模块安装机制，为什么输入 npm install 就可以自动安装对应的模块？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/node中并发和多线程的理解/" class="nav-link">
  node中并发和多线程的理解
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/node/浏览器和 Node 事件循环的区别/" class="nav-link">
  浏览器和 Node 事件循环的区别
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="网络编程" class="dropdown-title"><span class="title">网络编程</span> <span class="arrow down"></span></button> <button type="button" aria-label="网络编程" class="mobile-dropdown-title"><span class="title">网络编程</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http协议 http1.0、1.1、2.0 协议的区别？/" class="nav-link">
  http协议 http1.0、1.1、2.0 协议的区别？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/Http 状态码 301 和 302 的应用场景分别是什么/" class="nav-link">
  Http 状态码 301 和 302 的应用场景分别是什么
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/永久性重定向（301）和临时性重定向（302）对 SEO 有什么影响/" class="nav-link">
  永久性重定向（301）和临时性重定向（302）对 SEO 有什么影响
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http缓存/" class="nav-link">
  http缓存
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/介绍下前端加密的常见场景和方法/" class="nav-link">
  介绍下前端加密的常见场景和方法
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/HTTPS 中间人攻击/" class="nav-link">
  HTTPS 中间人攻击
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/接口如何防刷/" class="nav-link">
  接口如何防刷
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/浏览器缓存读取规则/" class="nav-link">
  浏览器缓存读取规则
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/websocket/" class="nav-link">
  websocket
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/TCP三次握手和四次挥手的理解/" class="nav-link">
  TCP三次握手和四次挥手的理解
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/cookie 和 token 都存放在 header 中，为什么不会劫持 token？/" class="nav-link">
  cookie 和 token 都存放在 header 中，为什么不会劫持 token？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/如何实现 token 加密/" class="nav-link">
  如何实现 token 加密
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/http/http2的多路复用/" class="nav-link">
  http2的多路复用
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="优化" class="dropdown-title"><span class="title">优化</span> <span class="arrow down"></span></button> <button type="button" aria-label="优化" class="mobile-dropdown-title"><span class="title">优化</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/重绘和回流/" class="nav-link">
  重绘和回流
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/防抖和节流  input 搜索如何防抖，如何处理中文输入/" class="nav-link">
  防抖和节流  input 搜索如何防抖，如何处理中文输入
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/webpack打包vue速度太慢怎么解决/" class="nav-link">
  webpack打包vue速度太慢怎么解决
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/如何优化首页的加载速度？首页白屏是什么问题引起的？如何解决呢/" class="nav-link">
  如何优化首页的加载速度？首页白屏是什么问题引起的？如何解决呢
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/vue 渲染大量数据时应该怎么优化？/" class="nav-link">
  vue 渲染大量数据时应该怎么优化？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/optimization/webpack 热更新原理，是如何做到在不刷新浏览器的前提下更新页面的/" class="nav-link">
  webpack 热更新原理，是如何做到在不刷新浏览器的前提下更新页面的
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="工程化" class="dropdown-title"><span class="title">工程化</span> <span class="arrow down"></span></button> <button type="button" aria-label="工程化" class="mobile-dropdown-title"><span class="title">工程化</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/文档工具/" class="nav-link">
  文档工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/git的workflow/" class="nav-link">
  git的workflow
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/接口测试工具/" class="nav-link">
  接口测试工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/敏捷开发工具/" class="nav-link">
  敏捷开发工具
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/线上bug的解决姿势/" class="nav-link">
  线上bug的解决姿势
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/ts的环境搭建/" class="nav-link">
  ts的环境搭建
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/Nginx/" class="nav-link">
  Nginx
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="多端开发" class="dropdown-title"><span class="title">多端开发</span> <span class="arrow down"></span></button> <button type="button" aria-label="多端开发" class="mobile-dropdown-title"><span class="title">多端开发</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/andorid和js的交互/" class="nav-link">
  andorid和js的交互
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/flutter/" class="nav-link">
  flutter
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/engineering/react-native/" class="nav-link">
  react-native
</a></li></ul></div></div><div class="nav-item"><div class="dropdown-wrapper"><button type="button" aria-label="面试题" class="dropdown-title"><span class="title">面试题</span> <span class="arrow down"></span></button> <button type="button" aria-label="面试题" class="mobile-dropdown-title"><span class="title">面试题</span> <span class="arrow right"></span></button> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现模糊搜索结果的关键词高亮显示/" class="nav-link">
  实现模糊搜索结果的关键词高亮显示
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现 convert 方法，把原始 list 转换成树形结构，要求尽可能降低时间复杂度/" class="nav-link">
  实现 convert 方法，把原始 list 转换成树形结构，要求尽可能降低时间复杂度
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/已知数据格式，实现一个函数 fn 找出链条中所有的父级 id/" class="nav-link">
  已知数据格式，实现一个函数 fn 找出链条中所有的父级 id
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/移动端 Retina 屏 1px 像素问题/" class="nav-link">
  移动端 Retina 屏 1px 像素问题
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/算法题之移动零/" class="nav-link">
  算法题之移动零
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/模拟实现一个深拷贝，并考虑对象相互引用以及 Symbol 拷贝的情况/" class="nav-link">
  模拟实现一个深拷贝，并考虑对象相互引用以及 Symbol 拷贝的情况
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片？/" class="nav-link">
  为什么通常在发送数据埋点请求的时候使用的是 1x1 像素的透明 gif 图片？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/算法题「旋转数组」/" class="nav-link">
  算法题「旋转数组」
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/两个数组合并成一个数组/" class="nav-link">
  两个数组合并成一个数组
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/a.b.c.d` 和 a[&quot;b&quot;][&quot;c&quot;][&quot;d&quot;]，哪个性能更高？/" class="nav-link">
  a.b.c.d 和 a[&quot;b&quot;][&quot;c&quot;][&quot;d&quot;]，哪个性能更高？
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/实现一个 sleep 函数/" class="nav-link">
  实现一个 sleep 函数
</a></li><li class="dropdown-item"><!----> <a href="/ztjy-technology/interview/如何把一个字符串的大小写取反（大写变小写小写变大写），例如 &quot;AbC&quot; 变成 &quot;aBc&quot; 。/" class="nav-link">
  如何把一个字符串的大小写取反（大写变小写小写变大写），例如 &quot;AbC&quot; 变成 &quot;aBc&quot; 。
</a></li></ul></div></div> <!----></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0"><p class="sidebar-heading open"><span>回调地狱以及意不解决方案</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/ztjy-technology/js/promise/" aria-current="page" class="active sidebar-link">打不死的回调地狱？</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#面试题" class="sidebar-link">面试题</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#说在前面" class="sidebar-link">说在前面</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#什么是回调地狱-what-is-callback-hell" class="sidebar-link">什么是回调地狱 （What is Callback Hell）</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#回调函数-root-of-evil" class="sidebar-link">回调函数 （Root of evil）</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#callback风格的一般约定" class="sidebar-link">Callback风格的一般约定</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#你可能不知道的回调函数的用法" class="sidebar-link">你可能不知道的回调函数的用法</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#给主流程一个承诺-promise" class="sidebar-link">给主流程一个承诺——Promise</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#时代的副产物-co" class="sidebar-link">时代的副产物——co</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#可能是终极解决方案-async-await" class="sidebar-link">可能是终极解决方案——async/await</a></li><li class="sidebar-sub-header"><a href="/ztjy-technology/js/promise/#同步-异步转换问题" class="sidebar-link">同步/异步转换问题</a></li></ul></li></ul></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><h1 id="打不死的回调地狱"><a href="#打不死的回调地狱" class="header-anchor">#</a> 打不死的回调地狱？</h1> <h2 id="面试题"><a href="#面试题" class="header-anchor">#</a> 面试题</h2> <h2 id="说在前面"><a href="#说在前面" class="header-anchor">#</a> 说在前面</h2> <p>相信每一个JSer都曾面临一个恼人的问题：回调地狱（Callback Hell）。本篇文章就简单地讲讲怎么去解决这个问题。同时形成更完善的认识和理解。</p> <h2 id="什么是回调地狱-what-is-callback-hell"><a href="#什么是回调地狱-what-is-callback-hell" class="header-anchor">#</a> <strong>什么是回调地狱 （What is Callback Hell）</strong></h2> <p>这儿有一篇文章能够详细地回答这个问题：<a href="http://callbackhell.com/" target="_blank" rel="noopener noreferrer">http://callbackhell.com/<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>我在这儿简单地翻译一下。</p> <blockquote><p>异步Javascript，或者使用回调函数的Javascript，很难直观地去理解。大量的代码看起来像这样：</p></blockquote> <div class="language-js line-numbers-mode"><pre class="language-js"><code>fs<span class="token punctuation">.</span><span class="token function">readdir</span><span class="token punctuation">(</span>source<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> files</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Error finding files: '</span> <span class="token operator">+</span> err<span class="token punctuation">)</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    files<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">filename<span class="token punctuation">,</span> fileIndex</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>filename<span class="token punctuation">)</span>
      <span class="token function">gm</span><span class="token punctuation">(</span>source <span class="token operator">+</span> filename<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">size</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err<span class="token punctuation">,</span> values</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
        <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span>
          console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Error identifying file size: '</span> <span class="token operator">+</span> err<span class="token punctuation">)</span>
        <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
          console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>filename <span class="token operator">+</span> <span class="token string">' : '</span> <span class="token operator">+</span> values<span class="token punctuation">)</span>
          aspect <span class="token operator">=</span> <span class="token punctuation">(</span>values<span class="token punctuation">.</span>width <span class="token operator">/</span> values<span class="token punctuation">.</span>height<span class="token punctuation">)</span>
          widths<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">width<span class="token punctuation">,</span> widthIndex</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
            height <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">round</span><span class="token punctuation">(</span>width <span class="token operator">/</span> aspect<span class="token punctuation">)</span>
            console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'resizing '</span> <span class="token operator">+</span> filename <span class="token operator">+</span> <span class="token string">'to '</span> <span class="token operator">+</span> height <span class="token operator">+</span> <span class="token string">'x'</span> <span class="token operator">+</span> height<span class="token punctuation">)</span>
            <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">resize</span><span class="token punctuation">(</span>width<span class="token punctuation">,</span> height<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>dest <span class="token operator">+</span> <span class="token string">'w'</span> <span class="token operator">+</span> width <span class="token operator">+</span> <span class="token string">'_'</span> <span class="token operator">+</span> filename<span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
              <span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Error writing file: '</span> <span class="token operator">+</span> err<span class="token punctuation">)</span>
            <span class="token punctuation">}</span><span class="token punctuation">)</span>
          <span class="token punctuation">}</span><span class="token punctuation">.</span><span class="token function">bind</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
        <span class="token punctuation">}</span>
      <span class="token punctuation">}</span><span class="token punctuation">)</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br></div></div><blockquote><p>看到这个金字塔形还有最后面的那些<code>})</code>了吧？对！这就是我们亲切地称为<strong>回调地狱</strong>的东西。</p></blockquote> <p>上面这篇文章后面的内容，读者可以自行去进行解读。它主要是通过程序设计层面的方式去解决回调地狱的问题。而本篇，主要从语言特性层面去进行解决。这应该才是最终最正确的方式。</p> <h2 id="回调函数-root-of-evil"><a href="#回调函数-root-of-evil" class="header-anchor">#</a> <strong>回调函数 （Root of evil）</strong></h2> <p>回调函数真的是一个让人又爱又恨的东西。一方面，它让Javascript能够非常简洁地实现异步函数；一方面，它又让你的代码难以去理解和维护——特别是当你陷入了回调魔咒的时候。</p> <p>首先，我们简单地理解一下回调函数的定义：</p> <ul><li>回调函数是一个闭包。</li> <li>回调函数是一个作为函数参数的函数</li> <li>回调函数是一段程序，它要在特定的时候去执行——当你调用它时。</li> <li>我们通过回调函数实现后置流程的多样化。</li> <li>回调函数的执行可以脱离同步流程，从而它是实现异步编程的方式。</li></ul> <p>回调函数的关键就在于“回调”两个字，将它的英文“Callback”拆解一下为“Call”和“back”就很清楚了：有某个流程——我们姑且叫它子流程——是脱离主流程的，但是我们又需要在主流程中确定当子流程达到一定阶段时要处理的后续流程，我们需要用回调函数。“Callback”的字面意思是“调用回来”——从子流程调用回主流程的过程。</p> <p>因为这样的一个特性，就必然地出现了回调函数嵌套回调函数……反复嵌套——然后回调地狱就形成了。</p> <h2 id="callback风格的一般约定"><a href="#callback风格的一般约定" class="header-anchor">#</a> <strong>Callback风格的一般约定</strong></h2> <p>确定的一点，任何一个程序流程都会有两种执行结果：正常返回计算出的最终结果、任何一处发生异常抛出错误。</p> <p>基于这一点，我们用一种通用的方式来约定回调函数应该怎么去写：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">callback</span><span class="token punctuation">(</span><span class="token parameter">error<span class="token punctuation">,</span> result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token comment">// 你处理错误的流程</span>
    console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token comment">// 你处理正确结果的流程</span>
    console<span class="token punctuation">.</span><span class="token function">info</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div><p>一般的回调函数为什么是两个参数？这是通过上述的原因约定俗成的结果。其中第一个参数代表子流程运行过程中发生的错误，而第二个参数指的是子流程正常运行的结果。一个接受该回调函数的函数可能长这样：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">shouldBeNumber</span><span class="token punctuation">(</span><span class="token parameter">value<span class="token punctuation">,</span> cb</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'number'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">cb</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'It`s not a number!'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">cb</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">'It`s a number.'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面这个函数，判断<code>value</code>是否是一个数字，如果不是，将会调用回调函数<code>cb</code>将错误传过去，就会在回调函数哪里<code>console.error</code>输出了。而如果是，则调用回调函数时<code>error</code>参数置为<code>null</code>，这样在回调函数中就能正常处理结果了。</p> <h2 id="你可能不知道的回调函数的用法"><a href="#你可能不知道的回调函数的用法" class="header-anchor">#</a> <strong>你可能不知道的回调函数的用法</strong></h2> <p>既然有一般约定，那么就一定有不符合约定的方案了。比如：</p> <p><strong>使用特定标识对结果进行区分的</strong></p> <p>定义回调函数的返回结果为以下结构：</p> <div class="language-ts line-numbers-mode"><pre class="language-ts"><code><span class="token keyword">interface</span> <span class="token class-name">CallbackResult</span> <span class="token punctuation">{</span>
  code<span class="token operator">:</span> <span class="token number">0</span> <span class="token operator">|</span> <span class="token number">1</span><span class="token punctuation">;</span>
  result<span class="token operator">?</span><span class="token operator">:</span> <span class="token builtin">any</span><span class="token punctuation">;</span>
  error<span class="token operator">?</span><span class="token operator">:</span> Error<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>这样通过code来进行判断，同样能够达到和一般约定一样的效果。</p> <p><strong>可以多次调用的回调函数</strong></p> <p>既然回调函数是作为参数传入函数并在需要的时候调用的，那么并不限定回调函数只能用来处理错误和结果。来看一个多次调用回调函数的例子：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">setInterval</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token number">1000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><p>前面我们解释过回调函数的定义了，那么很容易可以看出来<code>setInterval</code>这个函数的定义<code>(callback: Function, period: number): void</code>。毋庸置疑，第一个参数其实就是一个回调函数，并且这个回调函数每秒钟会被调用一次。</p> <p>利用这个方式，我们可以实现一个子流程，输出多次结果。是不是有一点像生成器（Generator）了？</p> <p><strong>可以不止一个回调函数</strong></p> <p>回调函数只是参数，那么我们的函数就支持传入多个回调函数。比如：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">function</span> <span class="token function">foo</span><span class="token punctuation">(</span><span class="token parameter">handleSucceed<span class="token punctuation">,</span> handleFailed</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  <span class="token keyword">if</span><span class="token punctuation">(</span>isMistake<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">handleFailed</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'It`s a mistake'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">handleSucceed</span><span class="token punctuation">(</span><span class="token string">'You got it!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token function">foo</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">result</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div><p>通过传入两个不同的回调函数，来分别处理成功的结果和错误的结果，是不是感觉代码更加清晰容易理解了呢？你将要了解到的<code>Promise</code>正是利用了这一点。</p> <p>还有更多的对于回调函数的巧用方式，只要你记住回调函数只是一个函数类型的参数。</p> <h2 id="给主流程一个承诺-promise"><a href="#给主流程一个承诺-promise" class="header-anchor">#</a> <strong>给主流程一个承诺——Promise</strong></h2> <p>随着ECMAScript的更新，引入了一个新的概念：<code>Promise</code>。</p> <p>一般使用<code>Promise</code>的方式如下：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">const</span> pms <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">resolve<span class="token punctuation">,</span> reject</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// do sth.</span>
  <span class="token keyword">if</span><span class="token punctuation">(</span>isMistake<span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'It`s a mistake'</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
    <span class="token keyword">return</span> <span class="token function">reject</span><span class="token punctuation">(</span><span class="token string">'You got it!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

pms<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">result</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token parameter">error</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br></div></div><p>有没有发现？和上面一种对回调函数的使用方式出奇的像？</p> <p>这里的<code>resolve</code>和<code>reject</code>正是两个回调函数，就如同前面一个例子里面的<code>handleSucceed</code>和<code>handleFailed</code>一样。而这两个回调函数的传入方式，从上一个例子的直接两个参数传入，变成了通过<code>then</code>方法和<code>catch</code>方法来进行传入。</p> <p>相比而言，<code>Promise</code>的方式更加语义化，更容易理解——给主流程留下一个承诺，在之后可以通过该承诺获得子流程的执行结果。</p> <p>同时，<code>Promise</code>还支持一种特殊的写法：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">new</span> <span class="token class-name">Promise</span><span class="token punctuation">(</span><span class="token parameter">resolve</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">result</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">return</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>result<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> world!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token parameter">result</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>上面这一段代码，将在控制台打印出<code>Hello world!</code>字符串。</p> <p><code>Promise</code>的<code>then</code>方法和<code>catch</code>方法本身也是返回一个<code>Promise</code>对象的，因此可以直接进行链式调用，并且后一次的<code>then</code>方法的回调函数的参数是前一次<code>then</code>方法返回的结果。</p> <p>通过这种方式，你会惊喜地发现——回调地狱就这么被解决了。</p> <p>所以，简而言之：<code>Promise</code>就是<code>callback</code>风格的一个语法糖（Grammar sugar），它通过实现链式调用的方式来将回调函数的嵌套扁平化来达到解决回调地狱的目的。</p> <p>而<code>Promise</code>的诞生，也为后面更进一步的优化奠定了基础。</p> <h2 id="时代的副产物-co"><a href="#时代的副产物-co" class="header-anchor">#</a> <strong>时代的副产物——co</strong></h2> <p>说到回调地狱的问题，就不得不提一下TJ开发的流程控制库<code>co</code>，你可以在Github查看它的源码——<a href="https://link.zhihu.com/?target=https%3A//github.com/tj/co" target="_blank" rel="noopener noreferrer">https://github.com/tj/co<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a>；</p> <p>就像作者所说的： It is a stepping stone towards the async/await proposal. ——它是一个通往<code>async/await</code>提案的里程碑。
使用<code>co</code>可以实现类似于这样的流程控制：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token function">co</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span>
  <span class="token comment">// yield any promise</span>
  <span class="token keyword">const</span> result1 <span class="token operator">=</span> <span class="token keyword">yield</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> result2 <span class="token operator">=</span> <span class="token keyword">yield</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>result1<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> world!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Hello world!</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span>onerror<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>居然用看似同步编程的方式，实现了异步编程。简直妙哉！</p> <h2 id="可能是终极解决方案-async-await"><a href="#可能是终极解决方案-async-await" class="header-anchor">#</a> <strong>可能是终极解决方案——async/await</strong></h2> <p>由于<code>co</code>的推进，<code>async/await</code>由此诞生。关于<code>Async Functions</code>的详细信息查看这里：<a href="https://link.zhihu.com/?target=https%3A//tc39.github.io/ecmascript-asyncawait/" target="_blank" rel="noopener noreferrer">https://tc39.github.io/ecmascript-asyncawait/<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>我们试着将上一个例子用<code>async/await</code>的形式进行编写：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">generateAPromise</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">const</span> result1 <span class="token operator">=</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token string">'Hello'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">const</span> result2 <span class="token operator">=</span> <span class="token keyword">await</span> Promise<span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>result1<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> world!</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>result2<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Hello world!</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token function">generateAPromise</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span>onerror<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>你会发现，我仅仅只是把<code>co</code>函数更换成了用<code>async</code>关键字声明的函数，把<code>yield</code>更换成了<code>await</code>。最后达到的效果一样，而在语法层面上又更加的简洁。</p> <p>细心的人会发现，我把这个<code>async</code>函数赋值给了一个变量<code>generateAPromise</code>，我正是要说明：<code>async</code>函数其实是一个返回值为<code>Promise</code>对象的函数。或者更准确地说：<code>async</code>关键字声明的函数被调用时，实质上是创建了一个<code>Promise</code>对象。</p> <p>而<code>await</code>则表示：执行后面的表达式——不论是异步还是同步的表达式——并获取到<code>resolve</code>的结果，如果执行出错，则抛出到外层<code>async</code>函数的<code>catch</code>回调中。<code>await</code>同时作为<code>Promise</code>中的<code>resolve</code>和<code>reject</code>工作了。</p> <p>因此，我们可以说，<code>async</code>函数其实是<code>Promise</code>的语法糖。并且它通过形似同步编程的形式，达成了前述<code>Promise</code>的链式调用。并以此解决回调地狱的问题——并且可能是最终的解决方案了，毕竟它看起来就像是同步编程呢。</p> <h2 id="同步-异步转换问题"><a href="#同步-异步转换问题" class="header-anchor">#</a> <strong>同步/异步转换问题</strong></h2> <p>在我们实际编码过程中，难免会碰到同步/异步流程互相转化的过程。通过以上的学习，我们拥有了非常方便地完成这个任务的桥梁——<code>Promise</code>。</p> <p>前面的一个例子实际上就是<code>异步 -&gt; 同步</code>的一个例子，那怎么实现<code>同步 -&gt; 异步</code>呢？看下面一个例子：</p> <div class="language-js line-numbers-mode"><pre class="language-js"><code><span class="token keyword">const</span> <span class="token function-variable function">genP1</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// do sth for long time.</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> <span class="token function-variable function">genP2</span> <span class="token operator">=</span> <span class="token keyword">async</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
 <span class="token comment">// another thing for long time.</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span>

Promise<span class="token punctuation">.</span><span class="token function">all</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token function">genP1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">genP2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'All done!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token parameter">error</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  console<span class="token punctuation">.</span><span class="token function">error</span><span class="token punctuation">(</span>error<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>通过这种写法，我们的两个耗时流程被包装成异步函数，它们通过<code>Promise.all</code>方法并行执行，由此减少了主流程的等待时长——由耗时最长的那个流程决定。</p> <p>我不再放更多的例子了，一切需要你自己去体会。你只需要知道一点——万变不离其宗——所有的语法糖本质都是回调函数，当你准确地理解了回调函数的意义和本质，Javascript的同步/异步编程就没有什么神秘而言了。</p></div> <footer class="page-edit"><!----> <div class="last-updated"><span class="prefix">Last Updated:</span> <span class="time">12/8/2020, 11:58:31 AM</span></div></footer> <!----> </main></div><div class="global-ui"></div></div>
    <script src="/ztjy-technology/assets/js/app.ec27be4d.js" defer></script><script src="/ztjy-technology/assets/js/2.98a51c7c.js" defer></script><script src="/ztjy-technology/assets/js/9.5bd259cf.js" defer></script>
  </body>
</html>
